package boot;

import cn.hutool.core.codec.Base64;
import lombok.experimental.UtilityClass;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;

import javax.crypto.Cipher;
import java.io.ByteArrayOutputStream;
import java.nio.charset.StandardCharsets;
import java.security.KeyFactory;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.Signature;
import java.security.interfaces.RSAPrivateKey;
import java.security.interfaces.RSAPublicKey;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.HashMap;
import java.util.Map;

/**
 * RSA工具类.
 *
 * @author zww
 * @version 1.0
 * @date 2022/12/14 17:50
 */
@Slf4j
@UtilityClass
public class RSAUtil {

    public static final String PUBLICKEYA = "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCNyRC4FftLDjxPDuOjaD4ncyg1wOTNE12mjR/+PB8LgXayuGIIEyPOetHvJbBV/OaoKzmq3kyA55/5JtO5gm/eOr+1mLizeROi4pKiGL45yvDI5waZP/5t2vH72QdcbbpZ/FeGeVKSoEha49eYgV4qIvHx7/UQK1/pY+qg50o8XQIDAQAB";
    public static final String PUBLICKEYB = "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDQ+2gxJa8kOw+2OT6ZljL+GYt1SePKX5+5Ppz2XvE8ZCdyibKTWD+Ziq0q9ICviQ+Qj7+avoNT3Je9nZletqK59VFBQdLaNwoyzhxNRZCRjtBF41KOMFUQT1NdCL60TTYYp1t95z5SWDnbn0uMcsngHPdtEz8D9V6KpMBvUhqJ3wIDAQAB";
    public static final String PRIVATEKEYA = "MIICdgIBADANBgkqhkiG9w0BAQEFAASCAmAwggJcAgEAAoGBAI3JELgV+0sOPE8O46NoPidzKDXA5M0TXaaNH/48HwuBdrK4YggTI8560e8lsFX85qgrOareTIDnn/km07mCb946v7WYuLN5E6LikqIYvjnK8MjnBpk//m3a8fvZB1xtuln8V4Z5UpKgSFrj15iBXioi8fHv9RArX+lj6qDnSjxdAgMBAAECgYAHyCpXwDUbqHtc53dNB7KpnaNuV9j2/IIeWw6PeKNHkWJ/aqlOwiYr9gfBtXLZNVk/j6fJngva41ufZCcovmT/WthZyYfdyQXpVJoj91A+6fnBXJEEdPP3oji+WsuaflarfXZdPBcnmNipm8uC9L1wVCZA5wvlGOTim+g75FJpLQJBAPh4DhIQ2RJcpElxxxRlV02gRdZnFgRLsTPyZcmZdpYURcMmDpYpgXFFPCJbABNyXiQE9KVQlp3tOpP8GtwgbYcCQQCSFTiCMlQKEpDRGx8YQoViAjNUxwKzlxS/dEO/uK1oikN4nc+t5bDj1nbC1JRtCffduAjxMhf+LuSPiJ8yyJ/7AkEAx+M1Vp1NNHNpC1jIEoVwlZdxd4mY8b2/Ag5oiqW1/Ai1EsikBXPAmdJcDTtuNTbVu15tPi017jjx3il7pklEMwJAELO2a0Rf0Wr//bhxHK/2WhdQUSAT0f6zE5SybkgqI2qzfiXk6cf6KoKJv4rYkYLzQPlapGEK1zf7T+HZ+c9wYwJAFB3nMzTDNZuHkVgr/LzKQ29J3rL6MzJxUwamaJmZvXhSORSa7KCVzGHFf9uacXtSvy00zNvuJE+qPsaMbed16Q==";
    public static final String PRIVATEKEYB = "MIICdwIBADANBgkqhkiG9w0BAQEFAASCAmEwggJdAgEAAoGBAND7aDElryQ7D7Y5PpmWMv4Zi3VJ48pfn7k+nPZe8TxkJ3KJspNYP5mKrSr0gK+JD5CPv5q+g1Pcl72dmV62orn1UUFB0to3CjLOHE1FkJGO0EXjUo4wVRBPU10IvrRNNhinW33nPlJYOdufS4xyyeAc920TPwP1XoqkwG9SGonfAgMBAAECgYAF9bWB+U4mXVzUWIuVe7gWkQnuHV5WriVy9fsp94kvrLeHR7AFDbY6nb7Ug8aLuv/ron3XXumF4bueF7mA/vpjAAC0zmtovX8lSXL98ifwOf4OYRsiYqBl0J3p8M+uKNqfyVerGHi6cyE+3K2S7oBL/cG37xsjODT4rRnJ6+0WoQJBAP4gX4ImqWVY6b+1hxewLMvYVhgdN6PJtxhVoGmioaCeQku47BW+vcT/vi74T34ogY7gKC8aCnRao0czKny8r88CQQDShdSVC4BGi4LG3TNTSR/f1K8e9vLdzFl8nCL6HFnThc5rDO/DAORy8YVpJJWQ4jn57WXs5puhlp3NQctg8HjxAkEAoWPD5C9Tg2a9RKAvhG7BdFAXFUdw75cKreyrbIr5ZL09U0PEw2aYtITKrp/KNE/bP/bB7m/2VcC+U1h+Uiuu5wJANr5aY0+UUcPhg/jtadDwGvJhf3nT+d0zv/WSGtvYd+qpnKjGiDzpKwKnIIpE4mIRlW4oPQxSmBhkfIWJb3HY4QJBANHGGn9l3EjPWIuHrW7XMkJ4uzIA1tzr2X+uodyCGe/bp5ioCHy1ohozrFoGOolyAXYtIO5dCIpCxUz7ZauCwK8=";


    /**
     * 加密算法RSA.
     */
    private static final String KEY_ALGORITHM = "RSA";

    /**
     * 签名算法.
     */
    private static final String SIGNATURE_ALGORITHM = "SHA1withRSA";

    /**
     * 密钥长度，DH算法的默认密钥长度是1024 密钥长度必须是64的倍数，在512到65536位之间.
     **/
    private static final int KEY_SIZE = 1024;

    /**
     * 随机生成密钥对.
     *
     * @return
     */
    public static Map<String, String> genKeyPair() {
        try {
            // KeyPairGenerator类用于生成公钥和私钥对，基于RSA算法生成对象
            KeyPairGenerator keyPairGen = KeyPairGenerator.getInstance(KEY_ALGORITHM);
            // 初始化密钥对生成器，密钥大小为96-1024位
            keyPairGen.initialize(KEY_SIZE);
            // 生成一个密钥对，保存在keyPair中
            KeyPair keyPair = keyPairGen.generateKeyPair();
            // 得到私钥
            RSAPrivateKey privateKey = (RSAPrivateKey) keyPair.getPrivate();
            // 得到公钥
            RSAPublicKey publicKey = (RSAPublicKey) keyPair.getPublic();
            // 得到公钥字符串
            String publicKeyString = Base64.encode(publicKey.getEncoded());
            // 得到私钥字符串
            String privateKeyString = Base64.encode(privateKey.getEncoded());
            Map<String, String> map = new HashMap<>();
            map.put("publicKey", publicKeyString);
            map.put("privateKey", privateKeyString);
            return map;
        } catch (Exception e) {
            log.error("生成密钥对异常", e);
        }
        return null;
    }

    /**
     * 公钥加密过程.
     *
     * @param publicKeyStr  公钥
     * @param plainTextData 明文数据
     * @return
     */
    public static byte[] encryptByPublicKey(String publicKeyStr, String plainTextData) throws Exception {
        if (StringUtils.isBlank(publicKeyStr)) {
            throw new Exception("加密公钥为空, 请设置");
        }
        try {
            return encryptByPublicKey(publicKeyStr, plainTextData.getBytes(StandardCharsets.UTF_8));
        } catch (Exception e) {
            log.error("公钥加密异常", e);
            throw new Exception("公钥加密异常");
        }
    }

    /**
     * 公钥加密过程.
     *
     * @param publicKeyStr  公钥
     * @param plainTextData 明文数据
     * @return
     */
    public static byte[] encryptByPublicKey(String publicKeyStr, byte[] plainTextData) throws Exception {
        if (StringUtils.isBlank(publicKeyStr)) {
            throw new Exception("加密公钥为空, 请设置");
        }
        try {
            return encrypt(loadPublicKeyByStr(publicKeyStr), plainTextData);
        } catch (Exception e) {
            log.error("公钥加密异常", e);
            throw new Exception("公钥加密异常");
        }
    }

    /**
     * 公钥加密过程.
     *
     * @param publicKey     公钥
     * @param plainTextData 明文数据
     * @return
     */
    public static byte[] encrypt(RSAPublicKey publicKey, byte[] plainTextData) throws Exception {
        if (publicKey == null) {
            throw new Exception("加密公钥为空, 请设置");
        }
        try {
            // 使用默认RSA
            Cipher cipher = Cipher.getInstance(KEY_ALGORITHM);
            cipher.init(Cipher.ENCRYPT_MODE, publicKey);
            return processData(cipher, plainTextData, KEY_SIZE / 8 - 11);
        } catch (Exception e) {
            log.error("公钥加密异常", e);
            throw new Exception("公钥加密异常");
        }
    }

    /**
     * 私钥解密过程.
     *
     * @param privateKeyStr 私钥
     * @param baseData      密文数据(base64加密)
     * @return 明文
     * @throws Exception 解密过程中的异常信息
     */
    public static byte[] decryptByPrivateKey(String privateKeyStr, String baseData) throws Exception {
        if (StringUtils.isBlank(privateKeyStr)) {
            throw new Exception("解密私钥为空, 请设置");
        }
        try {
            return decryptByPrivateKey(privateKeyStr, Base64.decode(baseData));
        } catch (Exception e) {
            log.error("私钥解密异常", e);
            throw new Exception("私钥解密异常");
        }
    }

    /**
     * 私钥解密过程.
     *
     * @param privateKeyStr 私钥
     * @param cipherData    密文数据
     * @return 明文
     * @throws Exception 解密过程中的异常信息
     */
    public static byte[] decryptByPrivateKey(String privateKeyStr, byte[] cipherData) throws Exception {
        if (StringUtils.isBlank(privateKeyStr)) {
            throw new Exception("解密私钥为空, 请设置");
        }
        try {
            // 获取私钥
            RSAPrivateKey privateKey = loadPrivateKeyByStr(privateKeyStr);
            return decrypt(privateKey, cipherData);
        } catch (Exception e) {
            log.error("私钥解密异常", e);
            throw new Exception("私钥解密异常");
        }
    }

    /**
     * 私钥解密过程.
     *
     * @param privateKey 私钥
     * @param cipherData 密文数据
     * @return 明文
     * @throws Exception 解密过程中的异常信息
     */
    public static byte[] decrypt(RSAPrivateKey privateKey, byte[] cipherData) throws Exception {
        if (privateKey == null) {
            throw new Exception("解密私钥为空, 请设置");
        }
        try {
            // 使用默认RSA
            Cipher cipher = Cipher.getInstance(KEY_ALGORITHM);
            cipher.init(Cipher.DECRYPT_MODE, privateKey);
            return processData(cipher, cipherData, KEY_SIZE / 8);
        } catch (Exception e) {
            log.error("私钥解密异常", e);
            throw new Exception("私钥解密异常");
        }
    }

    /**
     * RSA签名.
     *
     * @param content    待签名数据
     * @param privateKey 商户私钥
     * @return 签名值
     */
    public static String sign(byte[] content, String privateKey) {
        try {
            Signature signature = Signature.getInstance(SIGNATURE_ALGORITHM);
            signature.initSign(loadPrivateKeyByStr(privateKey));
            signature.update(content);
            byte[] signed = signature.sign();
            return Base64.encode(signed);
        } catch (Exception e) {
            log.error("生成签名异常", e);
        }
        return null;
    }

    /**
     * RSA签名.
     *
     * @param content    待签名数据(base64)
     * @param privateKey 商户私钥
     * @return 签名值
     */
    public static String sign(String content, String privateKey) {
        try {
            Signature signature = Signature.getInstance(SIGNATURE_ALGORITHM);
            signature.initSign(loadPrivateKeyByStr(privateKey));
            signature.update(Base64.decode(content));
            byte[] signed = signature.sign();
            return Base64.encode(signed);
        } catch (Exception e) {
            log.error("生成签名异常", e);
        }
        return null;
    }

    /**
     * RSA验签名检查.
     *
     * @param content   待签名数据
     * @param sign      签名值
     * @param publicKey 分配给开发商公钥
     * @return 布尔值
     */
    public static boolean doCheck(byte[] content, String sign, String publicKey) {
        try {
            Signature signature = Signature.getInstance(SIGNATURE_ALGORITHM);
            signature.initVerify(loadPublicKeyByStr(publicKey));
            signature.update(content);
            return signature.verify(Base64.decode(sign));
        } catch (Exception e) {
            log.error("验证签名异常", e);
        }
        return false;
    }

    /**
     * RSA验签名检查.
     *
     * @param content   待签名数据(base64)
     * @param sign      签名值
     * @param publicKey 分配给开发商公钥
     * @return 布尔值
     */
    public static boolean doCheck(String content, String sign, String publicKey) {
        try {
            Signature signature = Signature.getInstance(SIGNATURE_ALGORITHM);
            signature.initVerify(loadPublicKeyByStr(publicKey));
            signature.update(Base64.decode(content));
            return signature.verify(Base64.decode(sign));
        } catch (Exception e) {
            log.error("验证签名异常", e);
        }
        return false;
    }

    /**
     * 从字符串中加载公钥.
     *
     * @param publicKeyStr 公钥数据字符串
     * @return
     */
    private RSAPublicKey loadPublicKeyByStr(String publicKeyStr) throws Exception {
        try {
            byte[] buffer = Base64.decode(publicKeyStr);
            KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);
            X509EncodedKeySpec keySpec = new X509EncodedKeySpec(buffer);
            return (RSAPublicKey) keyFactory.generatePublic(keySpec);
        } catch (Exception e) {
            log.error("公钥异常", e);
            throw new Exception("公钥异常");
        }
    }

    /**
     * 获取 私钥.
     *
     * @param privateKeyStr 私钥数据字符串
     * @throws Exception 加载私钥时产生的异常
     */
    private RSAPrivateKey loadPrivateKeyByStr(String privateKeyStr) throws Exception {
        try {
            byte[] buffer = Base64.decode(privateKeyStr);
            PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(buffer);
            KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);
            return (RSAPrivateKey) keyFactory.generatePrivate(keySpec);
        } catch (Exception e) {
            log.error("私钥异常", e);
            throw new Exception("私钥异常");
        }
    }

    /**
     * 分段处理数据.
     *
     * @param cipher      密码算法
     * @param dataes      数据
     * @param segmentSize 分段大小（小于等于0不分段）
     * @return
     */
    private byte[] processData(Cipher cipher, byte[] dataes, int segmentSize) {
        byte[] decBytes = null;
        try {
            ByteArrayOutputStream out = new ByteArrayOutputStream();
            int inputLength = dataes.length;
            int offSet = 0;
            for (int i = 0; inputLength - offSet > 0; offSet = i * segmentSize) {
                byte[] cache;
                if (inputLength - offSet > segmentSize) {
                    cache = cipher.doFinal(dataes, offSet, segmentSize);
                } else {
                    cache = cipher.doFinal(dataes, offSet, inputLength - offSet);
                }
                out.write(cache, 0, cache.length);
                ++i;
            }
            decBytes = out.toByteArray();
            out.close();
        } catch (Exception e) {
            log.error("分段处理数据异常", e);
        }
        return decBytes;
    }

    /*public static void main(String[] args) throws Exception {
        // 请求端 生成公钥私钥
        //Map<String, String> genKeyPair = genKeyPair();
        // 请求端公钥
        String publicKey = "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCNyRC4FftLDjxPDuOjaD4ncyg1wOTNE12mjR/+PB8LgXayuGIIEyPOetHvJbBV/OaoKzmq3kyA55/5JtO5gm/eOr+1mLizeROi4pKiGL45yvDI5waZP/5t2vH72QdcbbpZ/FeGeVKSoEha49eYgV4qIvHx7/UQK1/pY+qg50o8XQIDAQAB";//genKeyPair.get("publicKey");
        // 请求端私钥
        String privateKey = "MIICdgIBADANBgkqhkiG9w0BAQEFAASCAmAwggJcAgEAAoGBAI3JELgV+0sOPE8O46NoPidzKDXA5M0TXaaNH/48HwuBdrK4YggTI8560e8lsFX85qgrOareTIDnn/km07mCb946v7WYuLN5E6LikqIYvjnK8MjnBpk//m3a8fvZB1xtuln8V4Z5UpKgSFrj15iBXioi8fHv9RArX+lj6qDnSjxdAgMBAAECgYAHyCpXwDUbqHtc53dNB7KpnaNuV9j2/IIeWw6PeKNHkWJ/aqlOwiYr9gfBtXLZNVk/j6fJngva41ufZCcovmT/WthZyYfdyQXpVJoj91A+6fnBXJEEdPP3oji+WsuaflarfXZdPBcnmNipm8uC9L1wVCZA5wvlGOTim+g75FJpLQJBAPh4DhIQ2RJcpElxxxRlV02gRdZnFgRLsTPyZcmZdpYURcMmDpYpgXFFPCJbABNyXiQE9KVQlp3tOpP8GtwgbYcCQQCSFTiCMlQKEpDRGx8YQoViAjNUxwKzlxS/dEO/uK1oikN4nc+t5bDj1nbC1JRtCffduAjxMhf+LuSPiJ8yyJ/7AkEAx+M1Vp1NNHNpC1jIEoVwlZdxd4mY8b2/Ag5oiqW1/Ai1EsikBXPAmdJcDTtuNTbVu15tPi017jjx3il7pklEMwJAELO2a0Rf0Wr//bhxHK/2WhdQUSAT0f6zE5SybkgqI2qzfiXk6cf6KoKJv4rYkYLzQPlapGEK1zf7T+HZ+c9wYwJAFB3nMzTDNZuHkVgr/LzKQ29J3rL6MzJxUwamaJmZvXhSORSa7KCVzGHFf9uacXtSvy00zNvuJE+qPsaMbed16Q==";//genKeyPair.get("privateKey");

        // 接收端
       // Map<String, String> keyPair = genKeyPair();
        // 接收端公钥
        String toPublicKey = "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDQ+2gxJa8kOw+2OT6ZljL+GYt1SePKX5+5Ppz2XvE8ZCdyibKTWD+Ziq0q9ICviQ+Qj7+avoNT3Je9nZletqK59VFBQdLaNwoyzhxNRZCRjtBF41KOMFUQT1NdCL60TTYYp1t95z5SWDnbn0uMcsngHPdtEz8D9V6KpMBvUhqJ3wIDAQAB";//keyPair.get("publicKey");
        // 接收端私钥
        String toPrivateKey = "MIICdwIBADANBgkqhkiG9w0BAQEFAASCAmEwggJdAgEAAoGBAND7aDElryQ7D7Y5PpmWMv4Zi3VJ48pfn7k+nPZe8TxkJ3KJspNYP5mKrSr0gK+JD5CPv5q+g1Pcl72dmV62orn1UUFB0to3CjLOHE1FkJGO0EXjUo4wVRBPU10IvrRNNhinW33nPlJYOdufS4xyyeAc920TPwP1XoqkwG9SGonfAgMBAAECgYAF9bWB+U4mXVzUWIuVe7gWkQnuHV5WriVy9fsp94kvrLeHR7AFDbY6nb7Ug8aLuv/ron3XXumF4bueF7mA/vpjAAC0zmtovX8lSXL98ifwOf4OYRsiYqBl0J3p8M+uKNqfyVerGHi6cyE+3K2S7oBL/cG37xsjODT4rRnJ6+0WoQJBAP4gX4ImqWVY6b+1hxewLMvYVhgdN6PJtxhVoGmioaCeQku47BW+vcT/vi74T34ogY7gKC8aCnRao0czKny8r88CQQDShdSVC4BGi4LG3TNTSR/f1K8e9vLdzFl8nCL6HFnThc5rDO/DAORy8YVpJJWQ4jn57WXs5puhlp3NQctg8HjxAkEAoWPD5C9Tg2a9RKAvhG7BdFAXFUdw75cKreyrbIr5ZL09U0PEw2aYtITKrp/KNE/bP/bB7m/2VcC+U1h+Uiuu5wJANr5aY0+UUcPhg/jtadDwGvJhf3nT+d0zv/WSGtvYd+qpnKjGiDzpKwKnIIpE4mIRlW4oPQxSmBhkfIWJb3HY4QJBANHGGn9l3EjPWIuHrW7XMkJ4uzIA1tzr2X+uodyCGe/bp5ioCHy1ohozrFoGOolyAXYtIO5dCIpCxUz7ZauCwK8=";//keyPair.get("privateKey");

        // -------------------------发起请求----------------------------
        //发送端逻辑  利用接收端的公钥加密要发送的数据  利用发送端的私钥去签名
        //请求数据 进行数据加密 加签名
        Map<String, String> map = new HashMap<>();
        map.put("age", "18");
        map.put("name", "张三");
        map.put("class", "二班111111111111");
        map.put("class1", "二班2222222222222");
        map.put("class2", "二班3333333333333");
        map.put("class3", "二班4444444444444");
        map.put("class4", "二班555555555555555");
        map.put("class5", "二班666666666666");
        map.put("class6", "二班7777777777777");
        map.put("class8", "二班88888888888");
        map.put("class7", "二班9999999999999999999999999999999999999999999999999999999999999999");
        String plainText = JSON.toJSONString(map);
        List<String> keys = new ArrayList<>(map.keySet());
        Collections.sort(keys);
        StringBuilder contents = new StringBuilder();
        JSONObject jsonObject = new JSONObject();
        for (int i = 0; i < keys.size(); i++) {
            String key = keys.get(i);
            String value = map.get(key);
            jsonObject.put(key,value);
            // 拼接时，不包括最后一个&字符
            if (i == keys.size() - 1) {
                contents.append(key).append("=").append(value);
            } else {
                contents.append(key).append("=").append(value).append("&");
            }
        }
        TreeMap<String, Object> sortedMap = new TreeMap<>();
        for (String key : jsonObject.keySet()) {
            sortedMap.put(key, jsonObject.get(key));
        }
        //System.out.println("加密前数据:" + keys);
        //System.out.println("排序后字符串:" + contents.toString());
        System.out.println("请求数据:" + plainText);
        JSONObject sortedJsonObject = new JSONObject(sortedMap);
        System.out.println("字典排序数据:" + sortedJsonObject.toJSONString());
        //System.out.println("加密前数据:" + plainText);
        // 接收端 公钥加密
        byte[] encrypt = encryptByPublicKey(toPublicKey, sortedJsonObject.toJSONString().getBytes(StandardCharsets.UTF_8));
        System.out.println("接收端公钥加密数据:" + Arrays.toString(encrypt));
        // base64加密
        String data = Base64.encode(encrypt);
        System.out.println("base64加密之后数据:" + data);
        // 签名 私钥
        String sign = sign(data, privateKey);
        System.out.println("发送端私钥加密签名:" + sign);
        // --------------------------收到请求---------------------------
        // 收到加密数据     接收端逻辑 Base64解密 发送端公钥验签  接收端私钥解密
        // 验签 请求端 公钥
        //String content = Arrays.toString(Base64.decode(data));
        //System.out.println("base64解密之后数据:" + content);

        // 请求端 公钥
        // 验签
        boolean doCheck = doCheck(data, sign, publicKey);
        System.out.println("发送端公钥验签:" + doCheck);

        // 接收端 公钥解密
        // 私钥
        byte[] decrypt = decryptByPrivateKey(toPrivateKey, data);
        System.out.println("接收端私钥解密后数据:" + new String(decrypt));
    }*/
    public static void main(String[] args) {

    }
}

